package Persistencia;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class Repository<T> implements IRepository<T> {
	private Session session;

	public Repository() {
		//this.session = HibernateUtil.getSessionFactory().openSession();
	}

	@SuppressWarnings("unchecked")
	public final List<T> getAll(final Class<T> clazz) throws RepositoryException {

		try {
			this.session = HibernateUtil.getSessionFactory().openSession();
			return (this.session.createCriteria(clazz).setFetchMode("lineItems", FetchMode.JOIN)  
                    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
.list());
		} catch (final HibernateException e) {
			throw new RepositoryException(e);
		} finally {
			this.session.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	public final List<T> getAll(final String queryExpression,
	        final QueryParameter... parameters) throws RepositoryException {
		final Query query;
		List<T> t = null;
		try {
			this.session = HibernateUtil.getSessionFactory().openSession();
			query = this.session.createQuery(queryExpression);
			
			for (QueryParameter queryParameter : parameters) {
				query.setParameter(queryParameter.getName(), queryParameter.getValue());
			}
			t = query.list();

		} catch (final HibernateException e) {
			throw new RepositoryException(e);
		} finally {
			this.session.close();
		}
		
		return t;
	}
	
	@SuppressWarnings("unchecked")
	public final T get(final Class<T> clazz, final Serializable id) throws RepositoryException {
		T t;
		try {
			this.session = HibernateUtil.getSessionFactory().openSession();
			t = ((T) this.session.get(clazz, id));
		} catch (final HibernateException e) {
			e.printStackTrace();//TODO REmove
			throw new RepositoryException(e);
		} finally {
			this.session.close();
		}
		return t;
	}

	@SuppressWarnings("unchecked")
	public final T get(final String queryExpression,
	        final QueryParameter... parameters) throws RepositoryException {
		T t;
		try {
			this.session = HibernateUtil.getSessionFactory().openSession();
			final Query query = this.session.createQuery(queryExpression);
			
			for (QueryParameter queryParameter : parameters) {
				query.setParameter(queryParameter.getName(), queryParameter.getValue());
			}
			t = (T)query.uniqueResult();

		} catch (final HibernateException e) {
			throw new RepositoryException(e);
		} finally {
			this.session.close();
		}
		return t;
	}
	
	public final boolean exists(final Class<T> clazz, final Serializable id) throws RepositoryException {
		return this.get(clazz, id) != null;
	}
	
	public final void saveOrUpdate(final T instance) throws RepositoryException {

		try {
			this.session = HibernateUtil.getSessionFactory().openSession();
			this.session.beginTransaction();
			this.session.saveOrUpdate(instance);
		} catch (final HibernateException e) {
			this.session.getTransaction().rollback();
			System.out.println("##########################################################");
			System.out.println("#                    HibernateException                  #");
			System.out.println(e.getMessage());
			System.out.println("##########################################################");
			e.printStackTrace();
			throw new RepositoryException(e);
		} finally {
			this.session.getTransaction().commit();
			this.session.close();
		}
	}

	public final void delete(final T instance) throws RepositoryException {
		try {
			this.session = HibernateUtil.getSessionFactory().openSession();
			this.session.delete(instance);
			this.session.flush();
		} catch (final HibernateException e) {
			throw new RepositoryException(e);
		} finally {
			this.session.clear();
			this.session.close();
		}
	}
}
